import os
import string

from kivy.logger import Logger

from kivy.metrics import dp
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.filechooser import FileChooserIconView, FileChooserListView
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.textinput import TextInput

from amgineirc.view.buttonLogView import ButtonLogView
from amgineirc.model.sortedDictList import SortedDictList


class _FormLabel(Label):
    def __init__(self, text, helptext='', **kargs):
        if len(helptext) > 0:
            kargs['text'] = "[b]%s[/b]\n%s" % (text, helptext)
        else:
            kargs['text'] = "[b]%s[/b]" % (text)
        kargs['markup'] = True
        Label.__init__(self, **kargs)
        self.bind(width=self.resize_text)
        
    def resize_text(self, *largs):
        self.text_size = (self.width, None)
        
class _TextInput(TextInput):
    def __init__(self, **kargs):
        if 'on_text_handler' in kargs:
            self._on_text_handler = kargs['on_text_handler']
            del(kargs['on_text_handler'])
        else:
            self._on_text_handler = None
        TextInput.__init__(self, **kargs)
                                         
    def on_text(self, instance, value):
        if self._on_text_handler is not None:
            self._on_text_handler(instance, value) 

class MessageView(object):
    def __init__(self):
        self._messageview_popup = None
        self._messageview_command_handler = None
        self._messageview_command_handler_largs = ()
        self._messageview_back_handler = None
        self._messageview_reload_handler = None
        self._messageview_post_command_handler = None
        self._messageview_post_command_true = ''
        self._messageview_post_command_handler_largs = ()
        self._text_input = None
        self._autocomplete_dict = None
        self._autocomplete_list = None
        # metrics
        self._spacing = 10
        self._padding = 10
        self._popup_width = 400
        self._part_width = self._popup_width - (2 * self._padding)
        self._text_width = self._part_width - 30
        self._part_height = 50

    # form label
        
    def buildFormLabel(self, text, helptext, **kargs):
        return _FormLabel(text, helptext, **kargs)

    # edit text popup

    def message_edit_line(self, title,
                          message,
                          default_text, text_handler, *text_handler_largs,
                          **kargs):
        """
        text_handler will receive text, *text_handler_largs
        autocomplete_dict=instance_of_SortedDictLIst
        """
        self._process_kargs(**kargs)
        self._autocomplete_dict = kargs.get('autocomplete_dict', None)
        if not isinstance(self._autocomplete_dict, SortedDictList):
            self._autocomplete_dict = None
        self._messageview_command_handler = text_handler
        self._messageview_command_handler_largs = text_handler_largs
        # title
        spaces = 0
        parts = 1
        height = 0
        # help
        using_help = len(message) > 0
        if using_help:
            intro = Label(text=message,
                          text_size=(dp(self._text_width), None))
            intro.texture_update()
            intro.size = intro.texture_size
            height += intro.texture_size[1]
            spaces += 1
        # parts : edit_line, submit button, cancel button = 3
        spaces += 3
        parts += 3
        # autocomplete list
        if self._autocomplete_dict is not None:
            spaces += 1
            parts += 3
        # other buttons
        if self._messageview_back_handler is not None:
            # back button
            spaces += 1
            parts += 1
        # calc height
        height += parts * self._part_height
        height += spaces * self._spacing
        height += 2 * self._padding
        # build the popup
        self._messageview_popup = Popup(title=title,
                                        size_hint=(None, None),
                                        size=(dp(self._popup_width), dp(height)),
                                        auto_dismiss=False)
        layout = BoxLayout(orientation='vertical',
                           padding=(dp(self._padding), dp(self._padding)),
                           spacing=dp(self._spacing))
        # add the parts to the layout
        if using_help:
            layout.add_widget(intro)
        self._text_input = _TextInput(text=default_text,
                                     multiline=False,
                                     on_text_validate=lambda *largs : None,
                                     size=(dp(self._part_width), dp(self._part_height)),
                                     on_text_handler=self.w_messageview_handle_text)
        layout.add_widget(self._text_input)
        # add the autocomplete widget
        if self._autocomplete_dict is not None:
            self._autocomplete_list = ButtonLogView(self.w_messageview_autocomplete_on_press,
                                         50, # min button height
                                         False, # scroll with adds
                                         size_hint=(None, None),
                                         size=(dp(self._part_width-(2 * self._padding)), dp(3 * self._part_height)))
            layout.add_widget(self._autocomplete_list)
        # buttons
        layout.add_widget(Button(text='Submit',
                                 on_press=self.w_messageview_text_submit_handler,
                                 size=(dp(self._part_width), dp(self._part_height))))
        if self._messageview_back_handler is not None:
            layout.add_widget(Button(text='Back',
                                     on_press=self.w_messageview_back_handler,
                                     size=(dp(self._part_width), dp(self._part_height))))
        layout.add_widget(Button(text='Cancel',
                                 on_press=self._messageview_popup.dismiss,
                                 size=(dp(self._part_width), dp(self._part_height))))
        self._messageview_popup.content = layout
        self._messageview_popup.open()

    # message popups

    def message_info(self, title, message, **kargs):
        """
        kargs options:
        back_handler=my_back_callback:
            * is the optional back button callback
            * the callback will only be sent params *command_handler_largs
            * if back_handler is provided then a back button will be displayed
        reload_handler=my_reload_callback:
            * is the optional reload button callback
            * the callback will only be sent params *command_handler_largs
            * if reaload_handler is provided then a reload button will be displayed
        """
        self._messageview_popup = Popup(title=title,
                                        size_hint=(None, None),
                                        size=(dp(400), dp(300)),
                                        auto_dismiss=False)
        self._process_kargs(**kargs)
        # title
        spaces = 0
        parts = 1
        height = 0
        # help
        using_help = len(message) > 0
        if using_help:
            intro = Label(text=message,
                          text_size=(dp(self._text_width), None),
                          size_hint=(None, None))
            intro.texture_update()
            intro.size = intro.texture_size
            height += intro.texture_size[1]
            spaces += 1
        # ok button
        spaces += 1
        parts += 1
        # other buttons
        if self._messageview_back_handler is not None:
            # back button
            parts += 1
            spaces += 1
        # calc height
        height += parts * self._part_height
        height += spaces * self._spacing
        height += 2 * self._padding
        # build the popup
        self._messageview_popup = Popup(title=title,
                                        size_hint=(None, None),
                                        size=(dp(self._popup_width), dp(height)),
                                        auto_dismiss=False)
        # layout
        layout = BoxLayout(orientation='vertical',
                           padding=(dp(self._padding), dp(self._padding)),
                           spacing=dp(self._spacing))
        # add the parts to the layout
        if using_help:
            layout.add_widget(intro)
        if self._messageview_back_handler is not None:
            layout.add_widget(Button(text='Back',
                                     on_press=self.w_messageview_back_handler,
                                     size=(dp(self._part_width), dp(self._part_height))))
        layout.add_widget(Button(text='Ok',
                                 on_press=self._messageview_popup.dismiss,
                                 size=(dp(self._part_width), dp(self._part_height))))
        self._messageview_popup.content = layout
        self._messageview_popup.open()

    def message_warn(self, message, **kargs):
        self.message_info('Warning', message, **kargs)
        
    def message_success(self, message, **kargs):
        self.message_info('Success', message, **kargs)

    # yes / no popup
        
    def question_yes_no(self, title, question, command_handler, *command_handler_largs, **kargs):
        """
        kargs options:
        question becomse 'help' in self.select_options
        see self.select_options
        
        command_handler is passed True/False, **kargs
        """
        self._messageview_post_command_handler = command_handler
        self._messageview_post_command_true = 'Yes'
        self._messageview_post_command_handler_largs = command_handler_largs
        kargs['help'] = question
        self.select_options(title,
                            ['Yes', 'No'],
                            self.w_messageview_boolean_popup_on_press,
                            **kargs)

    # select popups

    def select_options(self, title, commands, command_handler, *command_handler_largs, **kargs):
        """
        kargs options:
        help='text':
            * the text displayed below the title
        back_handler=my_back_callback:
            * is the optional back button callback
            * the callback will only be sent params *command_handler_largs
            * if back_handler is provided then a back button will be displayed
        reload_handler=my_reload_callback:
            * is the optional reload button callback
            * the callback will only be sent params *command_handler_largs
            * if reaload_handler is provided then a reload button will be displayed
        """
        self._messageview_command_handler = command_handler
        self._messageview_command_handler_largs = command_handler_largs
        self._process_kargs(**kargs)
        help = kargs.get('help', '')
        # title
        spaces = 0
        parts = 1
        height = 0
        # help
        using_help = len(help) > 0
        if using_help:
            intro = Label(text=help,
                          text_size=(dp(self._text_width), None),
                          size_hint=(None, None))
            intro.texture_update()
            intro.size = intro.texture_size
            height += intro.texture_size[1] + 20
            spaces += 1
        # cancel button
        spaces += 1
        parts += 1
        # scrolling command list
        spaces += 1
        parts += 3
        # other buttons
        if self._messageview_back_handler is not None:
            # back button
            spaces += 1
            parts += 1
        if self._messageview_reload_handler is not None:
            # reload button
            spaces += 1
            parts += 1
        # calc height
        height += parts * self._part_height
        height += spaces * self._spacing
        height += 2 * self._padding
        # build the popup
        self._messageview_popup = Popup(title=title,
                                        size_hint=(None, None),
                                        size=(dp(self._popup_width), dp(height)),
                                        auto_dismiss=False)
        # layout
        layout = BoxLayout(orientation='vertical',
                           padding=(dp(self._padding), dp(self._padding)),
                           spacing=dp(self._spacing))
        # add the parts to the layout
        if using_help:
            layout.add_widget(intro)
        # the command list
        command_list = ButtonLogView(self.w_messageview_button_on_press,
                                     50, # min button height
                                     False, # scroll with adds
                                     size_hint=(None, None),
                                     size=(dp(self._part_width-(2 * self._padding)), dp(3 * self._part_height)))
        layout.add_widget(command_list)
        for i in commands:
            command_list.vm_log(i)
        # buttons
        if self._messageview_back_handler is not None:
            layout.add_widget(Button(text='Back',
                                     on_press=self.w_messageview_back_handler,
                                     size=(dp(self._part_width), dp(self._part_height))))
        if self._messageview_reload_handler is not None:
            layout.add_widget(Button(text='Reload',
                                     on_press=self.w_messageview_reload_handler,
                                     size=(dp(self._part_width), dp(self._part_height))))
        layout.add_widget(Button(text='Cancel',
                                 on_press=self._messageview_popup.dismiss,
                                 size=(dp(self._part_width), dp(self._part_height))))
        self._messageview_popup.content = layout
        self._messageview_popup.open()

    def select_folder(self, title, root_path, path_handler, *path_handler_largs, **kargs):
        """
        kargs options:
        help='text':
            * the text displayed below the title
        back_handler=my_back_callback:
            * is the optional back button callback
            * the callback will only be sent params *path_handler_largs
            * if back_handler is provided then a back button will be displayed
        reload_handler=my_reload_callback:
            * is the optional reload button callback
            * the callback will only be sent params *path_handler_largs
            * if reaload_handler is provided then a reload button will be displayed
        """
        self._messageview_command_handler = path_handler
        self._messageview_command_handler_largs = path_handler_largs
        self._process_kargs(**kargs)
        help = kargs.get('help', '')
        # title
        spaces = 0
        parts = 1
        height = 0
        # help
        using_help = len(help) > 0
        if using_help:
            intro = Label(text=help,
                          text_size=(dp(self._text_width), None),
                          size_hint=(None, None))
            intro.texture_update()
            intro.size = intro.texture_size
            height += intro.texture_size[1] + 20
        # parts: 3 for path, select path, cancel
        #        4 for the scrolling command list
        spaces += 4
        parts += 7
        # other buttons
        if self._messageview_back_handler is not None:
            # back button
            spaces += 1
            parts += 1
        if self._messageview_reload_handler is not None:
            # reload button
            spaces += 1
            parts += 1
        # calc height
        height += parts * self._part_height
        height += spaces * self._spacing
        height += 2 * self._padding
        # build the popup
        self._messageview_popup = Popup(title=title,
                                        size_hint=(None, None),
                                        size=(dp(self._popup_width), dp(height)),
                                        auto_dismiss=False)
        # layout
        layout = BoxLayout(orientation='vertical',
                           padding=(dp(self._padding), dp(self._padding)),
                           spacing=dp(self._spacing))
        # add the parts to the layout
        if using_help:
            layout.add_widget(intro)
        # file chooser
        self.fc = FileChooserListView(rootpath=root_path,
                                      size_hint=(None, None),
                                      size=(dp(self._part_width-(2 * self._padding)), dp(4 * self._part_height)))
        layout.add_widget(self.fc)
        # path label
        self.path = Label(text=root_path,
                          valign='middle',
                          text_size=(dp(self._text_width), dp(self._part_height)),
                          size=(dp(self._text_width), dp(self._part_height)),
                          size_hint=(None, None))
        layout.add_widget(self.path)
        self.fc.bind(path=self.w_messageview_set_path_from_fc)
        # buttons
        layout.add_widget(Button(text='Select Current Path',
                                 on_press=self.w_messageview_select_fc_path,
                                 size=(dp(self._part_width), dp(self._part_height))))
        if self._messageview_back_handler is not None:
            layout.add_widget(Button(text='Back',
                                     on_press=self.w_messageview_back_handler,
                                     size=(dp(self._part_width), dp(self._part_height))))
        if self._messageview_reload_handler is not None:
            layout.add_widget(Button(text='Reload',
                                     on_press=self.w_messageview_reload_handler,
                                     size=(dp(self._part_width), dp(self._part_height))))
        layout.add_widget(Button(text='Cancel',
                                 on_press=self._messageview_popup.dismiss,
                                 size=(dp(self._part_width), dp(self._part_height))))
        self._messageview_popup.content = layout
        self._messageview_popup.open()

    def select_file(self, title, filters, root_path, path_handler, *path_handler_largs, **kargs):
        """
        kargs options:
        help='text':
            * the text displayed below the title
        back_handler=my_back_callback:
            * is the optional back button callback
            * the callback will only be sent params *path_handler_largs
            * if back_handler is provided then a back button will be displayed
        reload_handler=my_reload_callback:
            * is the optional reload button callback
            * the callback will only be sent params *path_handler_largs
            * if reaload_handler is provided then a reload button will be displayed
        """
        self._messageview_command_handler = path_handler
        self._messageview_command_handler_largs = path_handler_largs
        self._process_kargs(**kargs)
        help = kargs.get('help', '')
        # title
        spaces = 0
        parts = 1
        height = 0
        # height
        using_help = len(help) > 0
        if using_help:
            intro = Label(text=help,
                          text_size=(dp(self._text_width), None),
                          size_hint=(None, None))
            intro.texture_update()
            intro.size = intro.texture_size
            height += intro.texture_size[1] + 20
            spaces += 1
        # parts: 3 for path, select file, cancel
        #        4 for the scrolling command list
        spaces += 4
        parts = 7
        # other buttons
        if self._messageview_back_handler is not None:
            # back button
            spaces += 1
            parts += 1
        if self._messageview_reload_handler is not None:
            # reload button
            spaces += 1
            parts += 1
        # calc height
        height += parts * self._part_height
        height += spaces * self._spacing
        height += 2 * self._padding
        # build the popup
        self._messageview_popup = Popup(title=title,
                                        size_hint=(None, None),
                                        size=(dp(self._popup_width), dp(height)),
                                        auto_dismiss=False)
        # layout
        layout = BoxLayout(orientation='vertical',
                           padding=(dp(self._padding), dp(self._padding)),
                           spacing=dp(self._spacing))
        # add the parts to the layout
        if using_help:
            layout.add_widget(intro)
        # file chooser
        self.fc = FileChooserListView(rootpath=root_path,
                                      filters=filters,
                                      size_hint=(None, None),
                                      size=(dp(self._part_width-(2 * self._padding)), dp(4 * self._part_height)))
        layout.add_widget(self.fc)
        # path label
        self.path = Label(text=root_path,
                          valign='middle',
                          text_size=(dp(self._text_width), dp(self._part_height)),
                          size=(dp(self._text_width), dp(self._part_height)),
                          size_hint=(None, None))
        layout.add_widget(self.path)
        self.fc.bind(path=self.w_messageview_set_path_from_fc)
        # buttons
        layout.add_widget(Button(text='Select Current File',
                                 on_press=self.w_messageview_select_fc_file,
                                 size=(dp(self._part_width), dp(self._part_height))))
        if self._messageview_back_handler is not None:
            layout.add_widget(Button(text='Back',
                                     on_press=self.w_messageview_back_handler,
                                     size=(dp(self._part_width), dp(self._part_height))))
        if self._messageview_reload_handler is not None:
            layout.add_widget(Button(text='Reload',
                                     on_press=self.w_messageview_reload_handler,
                                     size=(dp(self._part_width), dp(self._part_height))))
        layout.add_widget(Button(text='Cancel',
                                 on_press=self._messageview_popup.dismiss,
                                 size=(dp(self._part_width), dp(self._part_height))))
        self._messageview_popup.content = layout
        self._messageview_popup.open()
        
    # widget methods

    def w_messageview_autocomplete_on_press(self, instance):
        # put the value in self._text_input.text
        self._text_input.text = instance.text
        
    def w_messageview_handle_text(self, instance, value):
        if self._autocomplete_dict is not None:
            # using the dictionary
            hint_list = self._build_hint_list(value)
            self._autocomplete_list.vm_clear_log()
            if len(hint_list) > 0:
                for i in hint_list:
                    self._autocomplete_list.vm_log(i)

    def w_messageview_boolean_popup_on_press(self, text):
        self._messageview_post_command_handler(text == self._messageview_post_command_true,
                                               *self._messageview_post_command_handler_largs)

    def w_messageview_set_path_from_fc(self, instance, value):
        self.path.text = value

    def w_messageview_button_on_press(self, instance):
        self._messageview_popup.dismiss()
        text = instance.text
        self._messageview_popup = None
        self._messageview_command_handler(instance.text, *self._messageview_command_handler_largs)

    def w_messageview_text_submit_handler(self, instance):
        text = self._text_input.text
        self._text_input = None
        self._messageview_popup.dismiss()
        self._messageview_popup = None
        self._messageview_command_handler(text, *self._messageview_command_handler_largs)

    def w_messageview_back_handler(self, instance):
        self._messageview_popup.dismiss()
        self._messageview_popup = None
        self._messageview_back_handler(*self._messageview_command_handler_largs)

    def w_messageview_reload_handler(self, instance):
        self._messageview_popup.dismiss()
        self._messageview_popup = None
        self._messageview_reload_handler(*self._messageview_command_handler_largs)

    def w_messageview_select_fc_path(self, instance):
        self._messageview_popup.dismiss()
        path = self.fc.path
        self._messageview_popup = None
        self._messageview_command_handler(path, *self._messageview_command_handler_largs)

    def w_messageview_select_fc_file(self, instance):
        if len(self.fc.selection) == 1:
            self._messageview_popup.dismiss()
            path = os.path.join(self.fc.path, self.fc.selection[0])
            self._messageview_popup = None
            self._messageview_command_handler(path, *self._messageview_command_handler_largs)

    # private methods
        
    def _process_kargs(self, **kargs):
        self._messageview_back_handler = kargs.get('back_handler', None)
        self._messageview_reload_handler = kargs.get('reload_handler', None)

    def _build_hint_list(self, value):
        hint_list = []
        if len(value) > 0:
            if value[-1] not in string.punctuation:
                key = self._autocomplete_dict.make_key(value)
                names = self._autocomplete_dict.get_sorted_names(key)
                if names is not None:
                    have_first = False
                    v = value.lower()
                    for i in names:
                        if i.lower().startswith(v):
                            hint_list.append(i)
                            have_first = True
                        else:
                            if have_first is True:
                                break
        return hint_list
